Fixed GH-16233: Observer segfault when calling user function in internal function via trampoline #16252
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
In the test, I have an internal
__callfunction for_ZendTestMagicCallForwardthat calls the global function with name$nameviacall_user_function. Note that observer writes the pointer to the previously observed frame in the last temporary of the new call frame (*prev_observed_frame).The following happens:
First, we call
$test->callee, this will be handled via a trampoline with T=2 for the two arguments. The call frame is allocated at this point. This call frame is not observed because it hasZEND_ACC_CALL_VIA_TRAMPOLINEset. Next we useZEND_CALL_TRAMPOLINEto call the trampoline, this reuses the stack frame allocated earlier with T=2, but this time it is observed. The pointer to the previous frame is written outside of the call frame becauseTis too small (should be 3). We are now in the internal function_ZendTestMagicCallForward::__callwhere we call the global functioncallee. This will push a new call frame which will overlap*prev_observed_frame. This value gets overwritten byzend_init_func_execute_datawhenEX(opline)is set because*prev_observed_frameoverlaps withEX(opline). From now on,*prev_observed_frameis corrupted. Whenzend_observer_fcall_endis called this will result in reading wrong value*prev_observed_frameintocurrent_observed_frame. This causes issues inzend_observer_fcall_end_allleading to the segfault we observe.Despite function with
ZEND_ACC_CALL_VIA_TRAMPOLINEnot being observed, the reuse of call frames makes problems whenTis not large enough. To fix this, we make sure to add 1 toTifZEND_OBSERVER_ENABLEDis true.